﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace TileMapTool
{
    /// <summary>
    /// This class get image source to compare and save tile of this
    /// </summary>
    public class BitmapService
    {
        
        public Bitmap Bitmap;
        public List<Tile> Tiles;//List tile with distinct bitmap
        public List<Bitmap> Bitmaps;
        public int ImageHeight;
        public int ImageWidth;

        public int TileWidth;
        public int TileHeight;
        public int TileSet;

        public BitmapService()
        {
           TileWidth = Tile.Width;
           TileHeight = Tile.Height;
           TileSet = TileWidth + 2;
        }

        public BitmapService(int TileWidth, int TileHeight)
        {
            this.TileWidth = TileWidth;
            this.TileHeight = TileHeight;
            TileSet = TileWidth + 2;
        }

        /// <summary>
        /// Get Tiles and bitmaps from image
        /// </summary>
        /// <param name="ImageFile"></param>
        public BitmapService(string ImageFile, int TileWidth, int TileHeight)
        {
            this.TileWidth = TileWidth;
            this.TileHeight = TileHeight;
            this.TileSet = TileWidth + 2;

            Image image = Image.FromFile(ImageFile);
            this.Bitmap = new Bitmap(image);
            this.Tiles = new List<Tile>();
            ImageHeight = (Bitmap.Height / this.TileHeight) * this.TileHeight;
            ImageWidth = (Bitmap.Width / this.TileWidth) * this.TileWidth;

            Bitmaps = new List<Bitmap>();

            int indexWidth;
            //Get list bitmap from image
            for (int i = 0; i < ImageHeight; i += this.TileWidth)
            {
                for (int j = 0; j < ImageWidth; j += this.TileHeight)
                {
                    indexWidth = j;
                    if (j > 0) indexWidth -= 1;
                    Bitmaps.Add(GetBitmapFromImage(this.Bitmap, indexWidth, i));
                }
            }

            //Get list tile with distinct bitmap from bitmaps
            Tile tile = new Tile(Bitmaps[0], 0);
            Tiles.Add(tile);
            foreach (Bitmap bitmap in Bitmaps)
            {
                int k = 0;
                foreach (Tile item in Tiles)
                {
                    if (IsSameBitmap(bitmap, item.Bitmap))
                    {
                        break;
                    }
                    else
                    {
                        k++;
                    }
                }
                if (k == Tiles.Count)
                {
                    Tiles.Add(new Tile(bitmap, Tiles.Count));
                }
            }
        }

        /// <summary>
        /// Save image tile to matrix in file
        /// </summary>
        /// <param name="FileName"></param>
        public void SaveTileSet(string FileName)
        {
            StreamWriter writer = new StreamWriter(FileName);

            //Lines of file
            string[] lines = new string[ImageHeight / this.TileHeight + 2];
            //Get matrix size and save in first line
            lines[0] = (ImageHeight / this.TileHeight).ToString() + " " + (ImageWidth / this.TileWidth).ToString() + " " + TileWidth.ToString() + " " + TileHeight.ToString();
            writer.WriteLine(lines[0]);

            int index = 0;
            for (int i = 0; i <= ImageHeight / this.TileHeight; i++)
            {
                for (int j = 0; j <= ImageWidth / (TileSet); j++)
                {
                    if (index < Bitmaps.Count)
                    {
                        lines[i + 1] += GetTileIdOfBitmap(Bitmaps[index++]) + " ";
                    }
                }
                writer.WriteLine(lines[i + 1]);
            }

            writer.Close();
        }

        public Bitmap GetTileSetBitmap()
        {
            int n = (int)Math.Sqrt(Tiles.Count) + 1;
            int width = (TileSet) * n;
            int height = this.TileHeight * (Tiles.Count / n + 1);
            int x = 0;
            int y = 0;
            Bitmap Result = new Bitmap(width, height);
            foreach (Tile tile in Tiles)
            {
                tile.Position = new Point(x, y);
                x += TileSet;
                if (x >= width)
                {
                    x = 0;
                    y += this.TileHeight;
                }
            }
            using (Graphics g = Graphics.FromImage(Result))
            {
                foreach (Tile tile in Tiles)
                {
                    g.DrawImage(tile.Bitmap, tile.Position.X, tile.Position.Y);
                }
            }

            return Result;

        }

        /// <summary>
        /// Load map from text file
        /// </summary>
        /// <param name="TextFile"></param>
        /// <param name="TileImageFile"></param>
        public Bitmap LoadMapFromFile(string TextFile, string TileImageFile)
        {
            Bitmap result = null;

            Stream stream = File.Open(TextFile, FileMode.Open, FileAccess.Read, FileShare.None);
            StreamReader reader = new StreamReader(stream);

            List<Tile> mapTiles = new List<Tile>();
            Bitmap bitmap = new Bitmap(Image.FromFile(TileImageFile));
            int maxHeight = (bitmap.Height / this.TileHeight) * this.TileHeight;
            int maxWidth = (bitmap.Width / this.TileWidth) * this.TileWidth;
            //Get tiles of image
            for (int i = 0; i < maxHeight; i += this.TileHeight)
            {
                for (int j = 0; j < maxWidth; j += TileSet)
                {
                    Bitmap bm = GetBitmapFromImage(bitmap, j, i);
                    Tile t = new Tile(bm, mapTiles.Count);
                    mapTiles.Add(t);
                }
            }

            //Get width and height of map
            string readingLine = reader.ReadLine();
            var line = readingLine.TrimEnd().Split(' ').ToList();
            int height = int.Parse(line[0]) * this.TileHeight;
            int width = int.Parse(line[1]) * this.TileWidth;
            Bitmap mapBitmap = new Bitmap(width, height);

            readingLine = reader.ReadLine();
            line = new List<string>();
            while (readingLine != null)
            {
                if (!string.IsNullOrEmpty(readingLine))
                {
                    line.AddRange(readingLine.TrimEnd().Split(' '));
                }
                readingLine = reader.ReadLine();
            }

            using (Graphics g = Graphics.FromImage(mapBitmap))
            {
                int index = 0;
                Bitmap tileBitmap = new Bitmap(this.TileWidth, this.TileHeight);
                for (int i = 0; i < height; i += this.TileHeight)
                {
                    for (int j = 0; j < width; j += this.TileWidth)
                    {
                        if (index < line.Count)
                        {
                            tileBitmap = GetBitmapFromListTile(int.Parse(line[index++]), mapTiles);
                            g.DrawImage(tileBitmap, j, i);
                        }
                    }
                }
            }
            result = mapBitmap;

            return result;
        }

        private Bitmap GetBitmapFromListTile(int Id, List<Tile> Tiles)
        {
            Bitmap result = new Bitmap(this.TileWidth, this.TileHeight);
            foreach (Tile t in Tiles)
            {
                if (t.Id == Id)
                {
                    result = t.Bitmap;
                    break;
                }

            }
            Graphics g = Graphics.FromImage(result);

            return result;
        }
        /// <summary>
        /// Check Same bitmap
        /// </summary>
        /// <param name="A"></param>
        /// <param name="B"></param>
        /// <returns>bool</returns>
        private bool IsSameBitmap(Bitmap A, Bitmap B)
        {
            if (A.Height == B.Height && A.Width == B.Width)
            {
                for (int i = 0; i < A.Width; i++)
                {
                    for (int j = 0; j < A.Height; j++)
                    {
                        if (A.GetPixel(i, j).ToString() != B.GetPixel(i, j).ToString())
                        {
                            return false;
                        }
                    }
                }

                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Get bit map from image
        /// </summar y>
        /// <param name="Bitmap"></param>
        /// <param name="WidthIndex"></param>
        /// <param name="HeightIndex"></param>
        /// <returns>Bitmap</returns>
        private Bitmap GetBitmapFromImage(Bitmap Bitmap, int WidthIndex, int HeightIndex)
        {

            Rectangle r = new Rectangle(new Point(WidthIndex, HeightIndex), new Size(TileSet, this.TileHeight));
            Bitmap result = Bitmap.Clone(r, Bitmap.PixelFormat);

            return result;
        }

        /// <summary>
        /// Get id of bitmap in tiles
        /// </summary>
        /// <param name="Bitmap"></param>
        /// <returns></returns>
        private int GetTileIdOfBitmap(Bitmap Bitmap)
        {
            foreach (Tile tile in Tiles)
            {
                if (IsSameBitmap(Bitmap, tile.Bitmap))
                {
                    return tile.Id;
                    break;
                }
            }

            return -1;
        }
    }
}
